package com.rgs.oauthauthenticationjwt.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * @author RenGaoshuai
 * @date 2024/4/9 16:20
 * @description oauth服务配置类
 * 需要满足两点：
 * 继承AuthorizationServerConfigurerAdapter
 * 标注 @EnableAuthorizationServer 注解
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;


    //配置5
    //令牌端点安全约束配置，比如 /oauth/token对哪些开放
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //开启 /oauth/token_key 验证端口权限
                .tokenKeyAccess("permitAll()")
                //开启 /oauth/check_token 验证端口权限
                .checkTokenAccess("permitAll()")
                //支持 client_id和clieng_secret 做登录认证
                .allowFormAuthenticationForClients();
    }

//    因此一些必要的配置是要认证中心分配给你的，比如客户端唯一Id、秘钥、权限。
//
//    客户端配置的存储也支持多种方式，比如内存、数据库，对应的接口为：
//    org.springframework.security.oauth2.provider.ClientDetailsService

    //配置1
    //客户端详细配置，比如密钥id
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
        //指定客户端唯一ID为myjszl
                .withClient("myjszl")
                //指定秘钥，使用加密算法加密了，秘钥为123
                .secret(new BCryptPasswordEncoder().encode("123"))
                //给客户端分配的资源权限，对应的是资源服务，比如订单这个微服务就可以看成一个资源，作为客户端肯定不是所有资源都能访问。
                .resourceIds("res1")
                //定义认证中心支持的授权类型，总共支持五种
                .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")
                //定义客户端的权限，这里只是一个标识，资源服务可以根据这个权限进行鉴权。
                .scopes("all")
                //是否需要授权，设置为true则不需要用户点击确认授权直接返回授权码,false跳转到授权页面
                .autoApprove(false)
                //授权码模式的回调地址
                .redirectUris("http://www.baidu.com");
    }




    //配置4
    //令牌访问端点配置
//    配置了授权码模式所需要的服务，AuthorizationCodeServices
//            配置了密码模式所需要的AuthenticationManager
//    配置了令牌管理服务，AuthorizationServerTokenServices
//    配置/oauth/token申请令牌的uri只允许POST提交。
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                //授权码模式 需要的配置
                .authorizationCodeServices(authorizationCodeServices())
                //密码模式 所需要的配置
                .authenticationManager(authenticationManager)
                //令牌服务，无论哪种模式都需要此服务
                .tokenServices(tokenServices())
                //token接口访问请求方式
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);


/*        spring Security框架默认的访问端点有如下6个：

/oauth/authorize：获取授权码的端点
                /oauth/token：获取令牌端点。
/oauth/confifirm_access：用户确认授权提交端点。
/oauth/error：授权服务错误信息端点。
/oauth/check_token：用于资源服务访问的令牌解析端点。
/oauth/token_key：提供公有密匙的端点，如果你使用JWT令牌的话。

        当然如果业务要求需要改变这些默认的端点的url，也是可以修改的，AuthorizationServerEndpointsConfigurer有一个方法，如下：

        public AuthorizationServerEndpointsConfigurer pathMapping(String defaultPath, String customPath)
        第一个参数：需要替换的默认端点url

        第二个参数：自定义的端点url*/

    }



    //配置2
    //授权码模式配置 ,用来颁布和删除授权码，当然授权码也支持多种方式存储，比如内存，数据库，这里暂时使用内存方式存储，代码如下
    //    使用授权码模式必须配置一个授权码服务，用来颁布和删除授权码，当然授权码也支持多种方式存储，比如内存，数据库，这里暂时使用内存方式存储
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        //暂时使用内存
        return new InMemoryAuthorizationCodeServices();

    }


    //配置3
    //配置令牌的服务，用来创建、获取、刷新令牌，代码如下：
    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices service = new DefaultTokenServices();

        //客户端配置策略
        service.setClientDetailsService(clientDetailsService);

        //令牌刷新
        service.setSupportRefreshToken(true);

        //令牌服务
        service.setTokenStore(tokenStore);

        //access_token过期时间
        service.setAccessTokenValiditySeconds(60*60*2);
        //refresh_token过期时间
        service.setRefreshTokenValiditySeconds(60*60*24*3);


        //令牌增强，使用jwtAccessTokenConverter
        service.setTokenEnhancer(jwtAccessTokenConverter);


        return service;
    }


}

